/**
 * @用途
 * @author 黄敏
 * @创建时间 2025-08-19 08:01
 **/
/**
 * 模块说明（dragDropUtil）
 * - 职责：集中管理拖拽/放置所需的通用类型与辅助工具；提供通用的“源数据解析 + 放置处理”能力，尽量减少组件内重复代码。
 * - 适用：当前用于 browser-tab（普通标签）与 browser-tab-group（分组头），sidebar-tab 由各自组件处理（位置计算差异较大）。
 * - 设计约束：
 *   1) 该模块仅提供“数据解析 + 调用约定”，不改变业务判断（如 canDrop 边界、允许边缘等）；
 *   2) handleResolvedDrop 不做权限/可放置校验，调用前应由 useDropHandle 的 canDrop 保障；
 *   3) 跨窗口拖拽依赖 main 侧能力（_pre_.browser.moveTabsToCurrentBrowser），该调用具有副作用。
 * - 命名约定：useDragHandle/useDropHandle 中的 dragType 字符串，必须与 DragData 的键一致，保证数据的类型安全匹配。
 * - 扩展建议：新增拖拽类型时，需同步扩展 DragData，并在对应组件的 acceptedTypes 中声明；如需通用解析，可仿照 resolveBrowserTabOrGroup 实现。
 */

import type { Input } from '@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types'

/**
 * 拖拽数据联合类型
 * - 每个 key 对应 useDragHandle 中声明的 dragType；
 * - 仅当该类型参与当前拖拽时，对应字段才会出现在拖拽数据中；
 * - 组件在 useDropHandle.canDrop/onDrop 中通过键访问数据来源。
 */
export type DragData = {
  'browser-tab'?: {
    data: F2.tab.Entity // 标签页对象
    inGroupId?: number // 如果在分组内，则记录分组ID
  }
  'browser-tab-group'?: F2.tab.Group
  'sidebar-tab'?: {
    id: number
    direction: 'left' | 'right'
    index: number
  }
  bookmark?: any
  'bookmark-folder'?: any
}
/**
 * 已支持的拖拽类型联合（键名与 dragType 严格对应）
 */
export type DragType = keyof DragData

/**
 * 计算鼠标相对于元素四边的像素距离
 * - 常用于 canDrop 中的边界/容差判定（例如：限制分组内部左右边界）
 * - 注意：该函数依赖浏览器布局信息，需在元素已挂载且可见时调用
 * @param event Pragmatic DnD 提供的输入事件（包含 clientX/clientY 等）
 * @param element 目标 HTML 元素
 * @returns 一个对象，包含 top/right/bottom/left 到元素边缘的距离（单位：px）
 */
export function calculateMouseDistanceToElement(event: Input, element: HTMLElement) {
  const rect = element.getBoundingClientRect()

  return {
    top: event.clientY - rect.top,
    right: rect.right - event.clientX,
    bottom: rect.bottom - event.clientY,
    left: event.clientX - rect.left
  }
}

/**
 * 统一解析 browser-tab / browser-tab-group 的拖拽源数据
 * - 用途：将多种来源的拖拽数据解析为统一的 ResolvedDragPayload，便于后续放置逻辑复用
 * - 安全性：仅做结构提取，不修改任何 store/状态
 * - 失败：当 source 中不存在上述两类数据时，返回 null
 */
/**
 * ResolvedDragPayload
 * - id: 被拖拽对象（标签或分组）的 id
 * - tab: 当源为对象本身（跨窗口或需要对象操作）时提供 TabOrGroup
 * - tabIds: 批量 id（用于跨窗口移动时一次性传给主进程）
 */

export type ResolvedDragPayload = {
  id: number
  tab?: F2.tab.TabOrGroup
  tabIds: number[]
}

/**
 * 解析标签或分组的拖拽源数据为标准载荷
 * @param source 仅包含 browser-tab 或 browser-tab-group 的拖拽数据子集
 * @returns ResolvedDragPayload；若不匹配则返回 null
 * 使用场景：在 onDrop 回调内先解析，再统一调用 handleResolvedDrop
 * 注意：不做 canDrop 校验与状态变更
 */
export function resolveBrowserTabOrGroup(
  source: Pick<DragData, 'browser-tab' | 'browser-tab-group'>
): ResolvedDragPayload | null {
  const browserTab = source['browser-tab']
  const browserTabGroup = source['browser-tab-group']
  if (browserTab) {
    const tab = browserTab.data as F2.tab.TabOrGroup
    const id = tab.id
    return { id, tab, tabIds: [id] }
  } else if (browserTabGroup) {
    const group = browserTabGroup as F2.tab.Group
    const id = group.id
    return { id, tab: group, tabIds: group.tabs.map(t => t.id) }
  }
  return null
}

/**
 * 在已解析载荷基础上执行放置操作（保持原有行为）
 * @param resolved ResolvedDragPayload|null
 * @param targetId 目标 tabId（可为空表示末尾）
 * @param after 是否插入到目标之后
 * @param isExternal 是否为跨窗口放置
 * @param move 注入的移动函数，一般为 tabStore.moveTabRelativeToAnother
 * 行为：
 * - isExternal 且存在 tab 对象：先 _pre_.browser.moveTabsToCurrentBrowser，再 move(tab,...)
 * - 否则：move(id,...)
 * 不做：权限/边界判断；由调用方的 canDrop 保证
 */
export function handleResolvedDrop(
  resolved: ResolvedDragPayload | null,
  targetId: number | undefined,
  after: boolean,
  isExternal: boolean | undefined,
  move: (dragged: number | F2.tab.TabOrGroup, targetId: number | undefined, after: boolean) => void
): void {
  if (!resolved) return
  if (isExternal) {
    if (resolved.tab) {
      // 将标签页移动到当前窗口
      _pre_.browser.moveTabsToCurrentBrowser(resolved.tabIds)
      move(resolved.tab, targetId, after)
    }
  } else if (resolved.id) {
    move(resolved.id, targetId, after)
  }
}
